package com.echat.serviceapigateway.security.service.impl;

import com.echat.commonlib.constant.ErrorCode;
import com.echat.commonlib.exception.exc.EchatSecurityException;
import com.echat.commonlib.util.SpringContextUtil;
import com.echat.serviceapigateway.security.config.CacheConfig;
import com.echat.serviceapigateway.security.constant.Constants;
import com.echat.serviceapigateway.security.mapper.AuthorizationMapper;
import com.echat.serviceapigateway.security.model.authorization.Authorization;
import com.echat.serviceapigateway.security.model.authorization.Resource;
import com.echat.serviceapigateway.security.service.AuthorizationService;
import com.echat.serviceapigateway.security.service.ResourceService;
import com.echat.serviceapigateway.security.service.UserService;
import com.echat.serviceapigateway.security.tool.AuthCodeUtil;
import com.echat.serviceapigateway.security.tool.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by chenmohan on 2018/08/31
 *
 * @description
 */
@Service
public class AuthorizationServiceImpl implements AuthorizationService {

    /**
     * 用户创建时候赋予的零权限(没有任何接口的权限)
     */
    private static final String INIT_AUTHCODE = "0";

    @Autowired
    AuthorizationMapper authorizationMapper;

    @Autowired
    ResourceService resourceService;

    @Autowired
    UserService userService;

    /**
     * 判断用户是否有权访问某个uri资源，由拦截器调用
     *
     * @param userId 用户id
     * @param uri       受保护的资源
     * @return true表示有权访问
     */
    @Override
    public boolean isAuthorized(Long userId, String uri) {
        //先尝试从缓存中直接判断结果
        /*
        String result = (String)redisTemplate.opsForHash().get(CacheConfig.CACHE_AUTHORIZED_URL + userId, uri);
        if(result!=null){
            return result.equals(Constants.TRUE);
        }
        */
        //该方法由拦截器调用，不需对传参作非空检查
        ResourceService exResourceService = (ResourceService) SpringContextUtil.getBean(resourceService.getClass());
        Resource resource = exResourceService.getByResourceUri(uri);
        if (resource == null) {   //如果为 Null表示没有对该资源的访问限制,那么视为所有请求都可以访问该资源
            return true;
        }
        //受保护资源的id
        Long targetId = resource.getId();

        //拿到当前用户的id
    //    UserService exUserService = (UserService) SpringContextUtil.getBean(userService.getClass());
   //     Long userId = exUserService.getUserByAccessKey(accessKey).getId();

        //拿到该用户的权限码
        AuthorizationService exAuthorizationService = (AuthorizationService) SpringContextUtil.getBean(this.getClass());
        String authCode = exAuthorizationService.getAuthCodeByUserId(userId);

        //匹配权限码，是否有访问该资源的权限
        boolean isAccess = AuthCodeUtil.check(authCode, targetId);
      //  redisTemplate.opsForHash().put(CacheConfig.CACHE_AUTHORIZED_URL + userId, uri, isAccess+"");
        if (isAccess) {
            return true;
        } else {
            throw new EchatSecurityException(ErrorCode.UNAUTHORIZED_ACCESS, "The uri[" + uri + "] has not been accessed because it lacks valid authentication credentials");
        }
    }

    @Override
    public List<Resource> listResourcesByUserId(Long userId) {
        if (userId == null) {
            return null;
        }
        Authorization authorization = authorizationMapper.selectByPrimaryKey(userId);
        if (authorization == null) {
            return null;
        }
        List<Long> resourceIds = AuthCodeUtil.getAuthIdList(authorization.getAuthCode());
        return resourceService.listByIds(resourceIds);
    }

    /**
     * 获取指定用户的权限码
     *
     * @param userId
     * @return
     */
    @Cacheable(value = CacheConfig.CACHE_USER_AUTH_CODE, key = "#userId")
    @Override
    public String getAuthCodeByUserId(Long userId) {
        List<Long> idList = listResourcesByUserId(userId).parallelStream()
                .map(e -> e.getId())
                .collect(Collectors.toList());
        Long[] ids = new Long[idList.size()];
        idList.toArray(ids);
        return AuthCodeUtil.genAuthCode(ids);
    }

    /**
     * 修改指定用户的权限码
     *
     * @param userId
     * @return
     */
    @CachePut(value = CacheConfig.CACHE_USER_AUTH_CODE, key = "#userId")
    @Override
    public String updateAuthCodeByUserId(Long userId, String authIds) {
        if (userId == null || authIds == null) {
            return Constants.EMPTY_STR;
        }
        AuthorizationService exService = (AuthorizationService) SpringContextUtil.getBean(this.getClass());
        //相同的权限code不再执行数据库操作
        String originCode = exService.getAuthCodeByUserId(userId);
        String updateAuthCode = AuthCodeUtil.getAuthCodeListByIds(authIds);
        if (updateAuthCode.equals(originCode)) {
            return Constants.EMPTY_STR;
        }
        Authorization update = Authorization.builder()
                .userId(userId)
                .modifyTime(LocalDateTime.now())
                .authCode(updateAuthCode).build();
        authorizationMapper.updateByPrimaryKeySelective(update);

        //直接清掉该用户的权限树，下次再用到时再重新加载
        SpringUtil.getStringRedisTemplate().delete(CacheConfig.CACHE_USER_AUTH_TREE + userId);
        //直接清掉该用户的认证鉴权缓存
        SpringUtil.clearUserAccessCahcer(userId);
        return updateAuthCode;
    }

    /**
     * 添加一个用户的授权情况记录
     *
     * @param userId
     * @return
     */
    @Override
    public Authorization addAuthorization(Long userId) {
        LocalDateTime now = LocalDateTime.now();
        Authorization insert = Authorization.builder().userId(userId)
                .authCode(INIT_AUTHCODE)
                .createTime(now)
                .modifyTime(now).build();
        authorizationMapper.insertSelective(insert);
        return insert;
    }

}
